dojo.provide('games.Memory');

dojo.require('ph.bluetooth');
dojo.require('ph.sound.Speech');
dojo.require('flickr.Flickr');

dojo.declare('games.Memory', null, {
  constructor: function(flickrKey, tags, Reader) {
    this._tags                  = tags;
    this._tagMap                = null;
    this._remainingCombinations = null;
    this._correctCombinations   = null;
    this._flickrUsers           = null;
    this._flickrPhotos          = null;
    this._selectedPhotos        = null;
    
    this._rfid    = new Reader();
    this._scanner = ph.bluetooth.scanner();
    this._flickr  = new flickr.Flickr(flickrKey);
    this._speech  = new ph.sound.Speech();
    
    this._rfid.open();
    this._rfid.led     = true;
    this._rfid.antenna = true;
    
    this.data = {
      status: 'starting',
      tag1: null,
      tag2: null,
    };
  },
  
  start: function() {
    this.findUsers()
      .addCallback(dojo.hitch(this, 'findPhotos'))
        .addErrback(dojo.hitch(this, 'start'))
      .addCallback(dojo.hitch(this, 'selectPhotos'))
      .addCallback(dojo.hitch(this, 'assignTags'))
      .addCallback(dojo.hitch(this, '_start'));
  },
  
  _start: function() {
    this._remainingCombinations = this._tags.length / 2;
    this._correctCombinations   = {};
    
    dojo.connect(this._rfid, 'onTagEnter', this, '_tagNotify');
    dojo.connect(this._rfid, 'onTagLeave', this, '_tagNotify');
    dojo.connect(this._rfid, 'onTagEnter', this, '_verifyAttempt');
    
    this.data.status = 'playing';
    this._speech.say('Okay, ready to play!');
  },
  
  findUsers: function() {
    this._flickrUsers = [];
    
    var info = {
      deferred: new dojo.Deferred(),
      connectID: null
    };
    dojo.connect(this._scanner, 'onAppear', this, '_deviceDetected');
    info.connectID = dojo.connect(this._scanner, 'onInquiryEnd', dojo.hitch(this, '_inquiryEnded', info));
    
    this._scanner.run();
    this._speech.say('Hi! Welcome to the Memory game. Hang on for a moment while photos are downloaded.');
    
    return info.deferred;
  },
  
  _deviceDetected: function(evt) {
    this._flickrUsers.push(evt.name);
  },
  
  _inquiryEnded: function(info) {
    if (this._flickrUsers.length == 0) {
      this._scanner.run();
      this._speech.say('Couldn\'t find any Bluetooth devices yet. Trying again.');
    } else {
      dojo.disconnect(this._scanner, 'onAppear', this, '_deviceDetected');
      dojo.disconnect(info.connectID);
      info.deferred.callback(true);
    }
  },
  
  findPhotos: function() {
    this._flickrPhotos = [];
    
    if (this._flickrUsers.length == 0) {
      this._speech.say('Couldn\'t find any Bluetooth devices. No photos could be downloaded, so the game is off…');
      return;
    }
    
    for (var i = 0; i < this._flickrUsers.length; i++) {
      var u = this._flickrUsers[i];
      
      try {
        var user   = this._flickr.people.findByUsername({
          username: flickr.Flickr.findUsernameByUrlName(u) || u
        }).user;
        
        if (!user) continue;
        
        var photos = this._flickr.people.getPublicPhotos({
          user_id:  user.nsid,
          per_page: this._tags.length
        }).photos;
        
        for (var j = 0; j < photos.photo.length; j++) {
          var photo = {
            user: user.username._content,
            url: flickr.Flickr.toPhotoUrl(photos.photo[j])
          };
          if (photo) this._flickrPhotos.push(photo);
        }
      } catch(e) {
        print(e);
      }
    }
    
    if (this._flickrPhotos.length == 0) {
      return new Error('No photos!');
    }
    
    return true;
  },
  
  selectPhotos: function() {
    this._selectedPhotos = [];
    
    while (this._selectedPhotos.length < this._tags.length / 2) {
      var ix = Math.floor(Math.random() * this._flickrPhotos.length);
      this._selectedPhotos.push(this._flickrPhotos.splice(ix, 1)[0]);
    }
    
    return true;
  },
  
  assignTags: function() {
    this._tagMap = {};
    
    var tags = dojo.clone(this._tags);
    var ix, photo, tag1, tag2;
    
    while (tags.length) {
      ix    = Math.floor(Math.random() * tags.length);
      tag1  = tags.splice(ix, 1)[0];
      ix    = Math.floor(Math.random() * tags.length);
      tag2  = tags.splice(ix, 1)[0];
      ix    = Math.floor(Math.random() * this._selectedPhotos.length);
      photo = this._selectedPhotos.splice(ix, 1)[0];
      
      this._tagMap[tag1]       = { tag: tag1 };
      this._tagMap[tag2]       = { tag: tag2 };
      this._tagMap[tag1].photo = photo;
      this._tagMap[tag1].pair  = tag2;
      this._tagMap[tag2].photo = photo;
      this._tagMap[tag2].pair  = tag1;
    }
    
    return true;
  },
  
  _tagNotify: function(evt) {
    if (evt.type == 'leave' && this.data.tag2 && this.data.tag2.tag == evt.tag) {
      this.data.tag1 = null;
      this.data.tag2 = null;
    }
    
    if (evt.type == 'enter') {
      if (!this.data.tag1) this.data.tag1 = this._tagMap[evt.tag];
      else if (this.data.tag1.tag != evt.tag) this.data.tag2 = this._tagMap[evt.tag];
    }
  },
  
  _verifyAttempt: function(evt) {
    if (this.data.tag1 && this.data.tag2 && this.data.tag1.pair == this.data.tag2.tag) {
      if (!(this.data.tag1.tag in this._correctCombinations))
      {
        this._remainingCombinations--;
        this._correctCombinations[this.data.tag1.tag] = true;
        this._correctCombinations[this.data.tag2.tag] = true;
      }
        
      if (this._remainingCombinations == 0) {
        this.data.status = 'finished';
        this._speech.say('You finished memory. Excellent.', 2000);
      } else {
        this._speech.say('Well done!', 2000);
      }
    }
  }
});
